#include "FilesystemReader.h"

#include <boost/filesystem.hpp>

#include <algorithm>

FilesystemReader::FilesystemReader(const std::string& root_path, 
                                   const std::shared_ptr<HashAlgorithm>& hash_alg,
                                   const std::shared_ptr<FormatQualifier>& formatQualifier) :
                                   root_path_(root_path),
                                   hash_alg_(hash_alg),
                                   formatQualifier_(formatQualifier) { }

std::vector<File> FilesystemReader::readFiles() const {

    namespace fs = boost::filesystem;

    std::vector<File> files;
    fs::path root_path(root_path_);

    try {
        if (fs::is_directory(root_path)) {
            fs::recursive_directory_iterator iteration_end;

            for (fs::recursive_directory_iterator rec_iter(root_path); 
                rec_iter != iteration_end; ++rec_iter) {

                if (fs::is_regular_file(rec_iter->status())) {
                    std::string name = rec_iter->path().string(); 
                    std::string normalized_name = fs::absolute(rec_iter->path()).string();
                    std::string fmt = formatQualifier_->getFormat(normalized_name);
                    std::string hash = hash_alg_->getHash(name);
                    std::string mime_type = formatQualifier_->getMimeType(fmt);
                    
                    File new_file(name, 
                                  rec_iter->path().extension().string(),
                                  fmt,
                                  fs::file_size(rec_iter->path()),
                                  hash,
                                  mime_type); 

                    files.push_back(new_file);
                }
            }
        } else {
            throw std::invalid_argument("Root path is not valid.");
        }
    } catch (const fs::filesystem_error &e) {
        throw FilesystemError(e.what());
    }

    return files;
}

FilesystemReader::FilesystemError::FilesystemError(const std::string& msg) : 
                                                   msg_(msg.c_str()) { }

FilesystemReader::FilesystemError::FilesystemError(const char* msg) : msg_(msg) { }

const char* FilesystemReader::FilesystemError::what() const noexcept {
    return msg_.c_str();
}
